package com.example.demo.study;

import com.example.demo.domain.entity.User;
import com.example.demo.service.interfaces.UserService;
import com.example.demo.utils.CsvExportUtil;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

@RestController
public class ArthasDemo {
    //    @Mock
    private StringRedisTemplate redisTemplate;
    private static final String A = "A";
    private static final String B = "B";

//    public void mock() {
//        redisTemplate = Mockito.mock(StringRedisTemplate.class);
//        HashOperations<String, Object, Object> hash = Mockito.mock(HashOperations.class);
//        Mockito.when(redisTemplate.opsForHash()).thenReturn(hash);
//        Mockito.when(hash.get(Mockito.anyString(), Mockito.any())).thenReturn(new Object());
//        Mockito.when(hash.multiGet(Mockito.anyString(), Mockito.any())).thenReturn(Lists.newArrayList(new Object()));
//    }

    public static void main(String[] args) {
        ArthasDemo arthasDemo = new ArthasDemo();
//        arthasDemo.mock();
//        arthasDemo.seeThread();
//        arthasDemo.justRun();
//        arthasDemo.deadLoop();
        arthasDemo.deadLock();
    }

    // 安装arthas-idea插件后，可以选中方法名右键复制
    // watch: 主要查看方法的入参、返回值，抛出异常，耗时等信息 方法调用结束后显示
    // trace：查看方法内部的操作的耗时时间
    // stack：查看方法调用栈
    private User getUser(String name, Integer age) {
        User user = new User();
        user.setName(name);
        user.setAge(age);
        return user;
    }

    @Resource
    UserService userService;

    /*
    tt 时空隧道
    选中方法名右键插件复制，tt -t 全类名 方法名 可以查看调用记录，每条记录有index
    tt -i index 可以查看接口调用详情
    tt -i index -p 可以回放接口调用，项目日志里打印对应日志
     */
    @GetMapping("/arthas/get/{id}")
    public User getUserById(@PathVariable Integer id) {
        return userService.getById(id);
    }

    @GetMapping("/arthas/testcsv")
    public Object testCsv() throws Exception {
        CsvExportUtil.main(null);
        return null;
    }

    // 死循环
    // dashboard 查看cpu、内存占用率很高
    // thread -n 3 查看前3个占用大的线程
    void deadLoop() {
        // 线上特定情况CPU打满，不知道原因，只能重启。后面有遇到这种情况反复
        while (true) {
            System.out.println("this is in deadLoop");
        }
    }

    // 死锁
    // thread 查看到有2条线程blocked
    // thread -b 查看死锁具体死锁信息
    void deadLock() {
        // 死锁争抢，CPU飙高
        Thread t1 = new Thread(() -> {
            synchronized (A) {
                System.out.println("线程1获取到资源A");
                // 保证线程2获取到资源B
                try {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (B) {
                        System.out.println("线程1尝试获取资源B");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        t1.setName("死锁1号");
        t1.start();

        Thread t2 = new Thread(() -> {
            synchronized (B) {
                System.out.println("线程2获取到资源B");
                try {
                    // 尝试获取资源A
                    synchronized (A) {
                        System.out.println("线程2尝试获取资源A");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        t2.setName("死锁2号");
        t2.start();

    }

    // dashboard
    @SneakyThrows
    // 将编译型异常转化为运行时异常，使得代码不用处理
    void justRun() {
        int i = 1;
        while (Instant.now().isBefore(Instant.now().plus(1, ChronoUnit.DAYS))) {
//            System.out.println("running");
            TimeUnit.SECONDS.sleep(1);
            System.out.println("get a user :" + getUser("userName" + i, i + 17));
            i++;
        }
    }

    // 查看线程 1.thread 2.序号
    void seeThread() {
        Thread t = new Thread(() -> {
            System.out.println("this is in a thread");
            try {
                TimeUnit.HOURS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.setName("THREAD_DEMO");
        t.start();
    }
}
